//----------------------------------------------------------------------------
// Anti-Grain Geometry (AGG) - Version 2.5
// A high quality rendering engine for C++
// Copyright (C) 2002-2006 Maxim Shemanarev
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://antigrain.com
// 
// AGG is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// 
// AGG is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with AGG; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
// MA 02110-1301, USA.
//----------------------------------------------------------------------------

#ifndef AGG_PIXFMT_RGB24_LCD_INCLUDED
#define AGG_PIXFMT_RGB24_LCD_INCLUDED

#include <string.h>
#include "agg_basics.h"
#include "agg_color_rgba.h"
#include "agg_rendering_buffer.h"
#include "agg_pixfmt_rgb.h"

namespace agg
{
	extern int g_agg_font_clip_org_x;
	extern int g_agg_font_clip_org_len;
	extern cover_type* g_agg_font_clip_org_cov;

    //=====================================================lcd_distribution_lut
    class lcd_distribution_lut
    {
    public:
        lcd_distribution_lut(double prim, double second, double tert)
        {
            double norm = 1.0 / (prim + second*2 + tert*2);
            prim   *= norm;
            second *= norm;
            tert   *= norm;
            for(unsigned i = 0; i < 256; i++)
            {
                m_primary[i]   = (unsigned char)floor(prim   * i);
                m_secondary[i] = (unsigned char)floor(second * i);
                m_tertiary[i]  = (unsigned char)floor(tert   * i);
            }
        }

        unsigned primary(unsigned v)   const { return m_primary[v];   }
        unsigned secondary(unsigned v) const { return m_secondary[v]; }
        unsigned tertiary(unsigned v)  const { return m_tertiary[v];  }

    private:
        unsigned char m_primary[256];
        unsigned char m_secondary[256];
        unsigned char m_tertiary[256];
    };

    //========================================================pixfmt_rgb565_lcd

    class pixfmt_rgb565_lcd
    {
    public:
        typedef rgba8 color_type;
        typedef rendering_buffer::row_data row_data;
        typedef color_type::value_type value_type;
        typedef color_type::calc_type calc_type;
		
        //--------------------------------------------------------------------
		pixfmt_rgb565_lcd() : m_rbuf(0), m_lut(0) {}
		void attach(rendering_buffer& rb, const lcd_distribution_lut& lut)
		{
			m_rbuf = &rb;
			m_lut = &lut;
		}

        pixfmt_rgb565_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut)
            : m_rbuf(&rb)
			, m_lut(&lut)
			//, m_gamma(1.0)
        {
			/*
			int i;
			for (i = 0; i < 32; ++i)
			{
				m_gamma_r[i] = WORD(i);
				m_gamma_b[i] = WORD(i) << 11;
			}
			for (i = 0; i < 64; ++i) m_gamma_g[i] = WORD(i) << 5;
			*/
		}
		
        //--------------------------------------------------------------------
        unsigned width()  const { return m_rbuf->width() * 3;  }
        unsigned height() const { return m_rbuf->height(); }
		
		
        //--------------------------------------------------------------------
        void blend_hline(int x, int y,
			unsigned len, 
			const color_type& c,
			int8u cover)
        {
            int16u* p = (int16u*)(m_rbuf->row_ptr(y) + x + x);
            int alpha = int(cover) * c.a;

			int8u cr = c.r >> 3, cg = c.g >> 2, cb = c.b >> 3;

            do
            {
				int16u val = *((int16u*)p);
				int8u r = int8u(val & 0x1F);
				int8u g = int8u((val >> 5) & 0x3F);
				int8u b = int8u((val >> 11) & 0x1F);


                r = (int8u)((((cr - r) * alpha) + (r << 16)) >> 16);
                g = (int8u)((((cg - g) * alpha) + (g << 16)) >> 16);
                b = (int8u)((((cb - b) * alpha) + (b << 16)) >> 16);
                
				val = int16(r);
				val |= (int16u(g) << 5);
				val |= (int16u(b) << 11);
				*p = val;
				*p++;
            }
            while(--len);
        }
		
		
        //--------------------------------------------------------------------
        void blend_solid_hspan(int x, int y,
			unsigned len, 
			const color_type& c,
			const int8u* covers)
        {
            int8u c3[2048*3];
            memset(c3, 0, g_agg_font_clip_org_len);
			
            int i;
            for(i = 0; i < int(g_agg_font_clip_org_len - 4); i++)
            {
                c3[i+0] += m_lut->tertiary(g_agg_font_clip_org_cov[i]);
                c3[i+1] += m_lut->secondary(g_agg_font_clip_org_cov[i]);
                c3[i+2] += m_lut->primary(g_agg_font_clip_org_cov[i]);
                c3[i+3] += m_lut->secondary(g_agg_font_clip_org_cov[i]);
                c3[i+4] += m_lut->tertiary(g_agg_font_clip_org_cov[i]);
            }

            covers = c3 + x - g_agg_font_clip_org_x;
			
            if(x < 0)
            {
                len += x;
				covers -= x;
                x = 0;
            }
			
            i = x % 3;

            int32u rgb[3] = { c.b >> 3, (c.g >> 2) << 5, (c.r >> 3) << 11 };

			int16u* p = (int16u*)(m_rbuf->row_ptr(y) + (x / 3) * 2);
			static const int32u mask[] = {0x1F, 0x7E0, 0xF800};
// 			static const int32u shift[] = {0, 5, 11};
			
			i = 2 - i;
			
            do 
            {
                int alpha = int(*covers++) * c.a;
                if(alpha)
                {
					int32u val = (int32u)*p;
					int32u val1 = val & mask[i];

					val1 = ((((rgb[i] - val1) * alpha) + (val1 * 64515))  / 64515);
					val1 &= mask[i];
					val &= ~mask[i];
					val |= val1;
					*p = val;
				}
				if(--i < 0) i = 2, p++;
            }
            while(--len);
        }
		
		/*
		void gamma(double g)
		{
			if (g == m_gamma)
				return;

			int i;
			for (i = 0; i < 32; ++i)
			{
				WORD tmp = WORD(pow(double(i) / 31, g) * 31);
				m_gamma_r[i] = tmp;
				m_gamma_b[i] = (tmp << 11);
			}
			for (i = 0; i < 64; ++i)
				m_gamma_g[i] = WORD(pow(double(i) / 63, g) * 63) << 5;

			m_gamma = g;
		}
		*/

    private:
        rendering_buffer* m_rbuf;
        const lcd_distribution_lut* m_lut;
		/*
		WORD	m_gamma_r[32];
		WORD m_gamma_g[64];
		WORD m_gamma_b[32];
		double m_gamma;
		*/
    };


    //========================================================pixfmt_rgb24_lcd
    class pixfmt_rgb24_lcd
    {
    public:
        typedef rgba8 color_type;
        typedef rendering_buffer::row_data row_data;
        typedef color_type::value_type value_type;
        typedef color_type::calc_type calc_type;

        //--------------------------------------------------------------------
		pixfmt_rgb24_lcd() : m_rbuf(0), m_lut(0) {}
		void attach(rendering_buffer& rb, const lcd_distribution_lut& lut)
		{
			m_rbuf = &rb;
			m_lut = &lut;
		}

        pixfmt_rgb24_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut)
            : m_rbuf(&rb),
              m_lut(&lut)
        {
        }

        //--------------------------------------------------------------------
        unsigned width()  const { return m_rbuf->width() * 3;  }
        unsigned height() const { return m_rbuf->height(); }


        //--------------------------------------------------------------------
        void blend_hline(int x, int y,
                         unsigned len, 
                         const color_type& c,
                         int8u cover)
        {
            int8u* p = m_rbuf->row_ptr(y) + x + x + x;
            int alpha = int(cover) * c.a;
            do
            {
                p[0] = (int8u)((((c.r - p[0]) * alpha) + (p[0] << 16)) >> 16);
                p[1] = (int8u)((((c.g - p[1]) * alpha) + (p[1] << 16)) >> 16);
                p[2] = (int8u)((((c.b - p[2]) * alpha) + (p[2] << 16)) >> 16);
                p += 3;
            }
            while(--len);
        }


        //--------------------------------------------------------------------
        void blend_solid_hspan(int x, int y,
                               unsigned len, 
                               const color_type& c,
                               const int8u* covers)
        {
            int8u c3[2048*3];
            memset(c3, 0, len+4);

            int i;
            for(i = 0; i < int(len); i++)
            {
                c3[i+0] += m_lut->tertiary(covers[i]);
                c3[i+1] += m_lut->secondary(covers[i]);
                c3[i+2] += m_lut->primary(covers[i]);
                c3[i+3] += m_lut->secondary(covers[i]);
                c3[i+4] += m_lut->tertiary(covers[i]);
            }

            x -= 2;
            len += 4;

            if(x < 0)
            {
                len += x;
                x = 0;
            }

            covers = c3;
            i = x % 3;

            int8u rgb[3] = { c.r, c.g, c.b };
            int8u* p = m_rbuf->row_ptr(y) + x;

            do 
            {
                int alpha = int(*covers++) * c.a;
                if(alpha)
                {
                    if(alpha == 255*255)
                    {
                        *p = (int8u)rgb[i];
                    }
                    else
                    {
                        *p = (int8u)((((rgb[i] - *p) * alpha) + (*p << 16)) >> 16);
                    }
                }
                ++p;
                ++i;
                if(i >= 3) i = 0;
            }
            while(--len);
        }


    private:
        rendering_buffer* m_rbuf;
        const lcd_distribution_lut* m_lut;
    };

	//========================================================pixfmt_rgb24_lcd
	class pixfmt_rgba32_lcd
	{
	public:
		typedef rgba8 color_type;
		typedef rendering_buffer::row_data row_data;
		typedef color_type::value_type value_type;
		typedef color_type::calc_type calc_type;

		//--------------------------------------------------------------------
		pixfmt_rgba32_lcd() : m_rbuf(0), m_lut(0) {}
		void attach(rendering_buffer& rb, const lcd_distribution_lut& lut)
		{
			m_rbuf = &rb;
			m_lut = &lut;
		}

		pixfmt_rgba32_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut)
			: m_rbuf(&rb),
			m_lut(&lut)
		{
		}

		//--------------------------------------------------------------------
		unsigned width()  const { return m_rbuf->width() * 3;  }
		unsigned height() const { return m_rbuf->height(); }


		//--------------------------------------------------------------------
		void blend_hline(int x, int y,
			unsigned len, 
			const color_type& c,
			int8u cover)
		{
			int8u* p = m_rbuf->row_ptr(y) + x + x + x;
			int alpha = int(cover) * c.a;
			do
			{
				p[0] = (int8u)((((c.r - p[0]) * alpha) + (p[0] << 16)) >> 16);
				p[1] = (int8u)((((c.g - p[1]) * alpha) + (p[1] << 16)) >> 16);
				p[2] = (int8u)((((c.b - p[2]) * alpha) + (p[2] << 16)) >> 16);
				p += 3;
			}
			while(--len);
		}


		//--------------------------------------------------------------------
		void blend_solid_hspan(int x, int y,
			unsigned len, 
			const color_type& c,
			const int8u* covers)
		{
			int8u c3[2048*3];
			memset(c3, 0, g_agg_font_clip_org_len);

			int i;
			for(i = 0; i < int(g_agg_font_clip_org_len - 4); i++)
			{
				c3[i+0] += m_lut->tertiary(g_agg_font_clip_org_cov[i]);
				c3[i+1] += m_lut->secondary(g_agg_font_clip_org_cov[i]);
				c3[i+2] += m_lut->primary(g_agg_font_clip_org_cov[i]);
				c3[i+3] += m_lut->secondary(g_agg_font_clip_org_cov[i]);
				c3[i+4] += m_lut->tertiary(g_agg_font_clip_org_cov[i]);
			}

			covers = c3 + x - g_agg_font_clip_org_x;

			if(x < 0)
			{
				len += x;
				covers -= x;
				x = 0;
			}

			i = x % 3;

			int32u rgb[3] = { c.r, c.g, c.b };
			int8u* p = ((int8u*)m_rbuf->row_ptr(y) + (x / 3) * 4) + i;

			do 
			{
				int alpha = int(*covers++) * c.a;

				int32u val = (int32u)*p;
				*p++ = (int8u)((((rgb[i] - val) * alpha) + (val * 64515))  / 64515);

				if (++i == 3) i = 0, p++;
			}
			while(--len);
		}


	private:
		rendering_buffer* m_rbuf;
		const lcd_distribution_lut* m_lut;
	};

class pixfmt_bgra32_lcd
{
public:
	typedef rgba8 color_type;
	typedef rendering_buffer::row_data row_data;
	typedef color_type::value_type value_type;
	typedef color_type::calc_type calc_type;

	//--------------------------------------------------------------------
	pixfmt_bgra32_lcd() : m_rbuf(0), m_lut(0) {}
	void attach(rendering_buffer& rb, const lcd_distribution_lut& lut)
	{
		m_rbuf = &rb;
		m_lut = &lut;
	}
	pixfmt_bgra32_lcd(rendering_buffer& rb, const lcd_distribution_lut& lut)
		: m_rbuf(&rb),
		m_lut(&lut)
	{
	}

	//--------------------------------------------------------------------
	unsigned width()  const { return m_rbuf->width() * 3;  }
	unsigned height() const { return m_rbuf->height(); }


	//--------------------------------------------------------------------
	void blend_hline(int x, int y,
		unsigned len, 
		const color_type& c,
		int8u cover)
	{
		int8u* p = m_rbuf->row_ptr(y) + x + x + x;
		int alpha = int(cover) * c.a;
		do
		{
			p[0] = (int8u)((((c.b - p[2]) * alpha) + (p[2] << 16)) >> 16);
			p[1] = (int8u)((((c.g - p[1]) * alpha) + (p[1] << 16)) >> 16);
			p[2] = (int8u)((((c.r - p[0]) * alpha) + (p[0] << 16)) >> 16);
			p += 3;
		}
		while(--len);
	}


	//--------------------------------------------------------------------
	void blend_solid_hspan(int x, int y,
		unsigned len, 
		const color_type& c,
		const int8u* covers)
	{
		int8u c3[2048*3];
		memset(c3, 0, g_agg_font_clip_org_len);

		int i;
		for(i = 0; i < int(g_agg_font_clip_org_len - 4); i++)
		{
			c3[i+0] += m_lut->tertiary(g_agg_font_clip_org_cov[i]);
			c3[i+1] += m_lut->secondary(g_agg_font_clip_org_cov[i]);
			c3[i+2] += m_lut->primary(g_agg_font_clip_org_cov[i]);
			c3[i+3] += m_lut->secondary(g_agg_font_clip_org_cov[i]);
			c3[i+4] += m_lut->tertiary(g_agg_font_clip_org_cov[i]);
		}

		covers = c3 + x - g_agg_font_clip_org_x;

		if(x < 0)
		{
			len += x;
			covers -= x;
			x = 0;
		}

		i = x % 3;

		int32u rgb[3] = { c.r, c.g, c.b };
		int8u* xstart = (int8u*)m_rbuf->row_ptr(y);
		int8u* p = (xstart + (x / 3) * 4) + i;

		do 
		{
			int alpha = int(*covers++) * c.a;

			int8u* q = p;
			int mode = (q - xstart) % 4;
			q -= mode + mode - 2;	// q = xstart + delta - mode + (2 - mode)

			int32u val = (int32u)*q;
			*q = (int8u)((((rgb[i] - val) * alpha) + (val * 64515))  / 64515);
			p++;

			if (++i == 3) i = 0, p++;
		}
		while(--len);
	}


private:
	rendering_buffer* m_rbuf;
	const lcd_distribution_lut* m_lut;
};

}
#endif

